package com.adee.jdk8;

import java.util.Arrays;
import java.util.List;

/*
* Lambda表达式
* 定义：Lambda表达式是一个语法糖，表示一个函数式接口类型的对象。
* 写法：
*   (String s, String s1) -> {int i = 1; return s+i+s1;}
*   s -> "=="+s; // 参数类型可推断时，可省略参数类型，代码只有一行时，可省略return关键字
* 原理：Lambda表达式会被编译器转为一个new一个函数式接口的匿名实现类。
*
* */
public class Test001_LambdaExpression {
    public static void main(String[] args) {
        test1();
    }

    public static void test1() {
        execute(s -> {
            System.out.println("使用[" + s + "]工作");
        }, "water");

        // Lambda表达式就是一个函数式接口类型的对象
        Worker<String> worker = s -> System.out.println("我也使用[" + s + "]工作");
        execute(worker, "water");

        System.out.println(worker.getClass()); // class com.adee.jdk8.Test001_LambdaExpression$$Lambda$2/0x0000000100061040
        System.out.println(worker.getClass().getSuperclass()); // class java.lang.Object
        System.out.println(worker.getClass().getInterfaces()[0]); // interface com.adee.jdk8.Worker

    }

    // 声明为函数式接口类型的参数，都可以传入Lambda表达式
    public static void execute(Worker worker, String material) {
        System.out.println("准备工作");
        worker.work(material);
        System.out.println("工作完成");
    }

    /*
    * 函数式接口实例的创建方式：Lambda表达式、方法引用、构造方法引用。
    * */
    public static void test2() {
        List<Integer> list = Arrays.asList(1, 2, 10, 3, 9, 7, 5);
        // List.forEach方法需要传入一个函数式接口类型的参数，可以用Lambda表达式
        list.forEach(v -> System.out.println(v));
        System.out.println("-------");
        // 使用方法引用，方法引用是一个函数式接口类型的对象
        list.forEach(System.out::println);
    }
}

// 函数式接口:只有一个方法的接口
/*
* 函数式接口：
* 1. 如果一个接口只有一个抽象方法，那么这个接口就是函数式接口；
* 2. 函数式接口上加不加@FunctionalInterface都不影响它实际是否为一个函数式接口；
* 3. 若实际的函数式接口加上@FunctionalInterface，无论该接口是否被使用，编译时会检查该接口是否为一个函数式接口；
* 4. 若实际的函数式接口不加@FunctionalInterface，该函数式接口本身不被编译器检查，除非该接口被当作函数式接口在别处
*    使用了，编译器才会检查该接口是否为函数式接口。
* 5. @FunctionalInterface注解的用途只有一个，就是在接口没有被使用的情况下，也会检查该接口是否满足函数式接口的条件。；
* 6. 函数式接口中，出自定义的唯一一个抽象方法外，允许出现Object类中的方法，如public String toString(); public boolean equals(Object o)。
*
* 根据FunctionalInterface源码中的注释，函数式接口实例创建方式有三种：
*   1. Lambda表达式；
*   2. 方法引用（方法引用时Lambda表达式的一个语法糖，恰巧有一个现有的方法满足逻辑）；
*       方法引用分为4类：
*           1) 类名::静态方法名；
*           2) 引用名(对象名)::实例方法名；
*           3) 类名::实例方法名；
*           4) 构造方法引用，类名::new 。
*   3. 构造方法引用。
* */
@FunctionalInterface
interface Worker<T> {
    void work(T name);
    // 接口中default方法在实现类中不必重写，可以动态扩展接口而不必修改已有实现类
    default public void eat() {
        System.out.println(this + ":什么都吃");
    }
    default public void drink() {
        System.out.println(this + ":什么都喝");
    }
    // void sleep();
    // 允许出现Object类中的方法
    String toString();
    boolean equals(Object obj);
}

